import { isNilOrEmpty, isObject, notNilEmpty } from 'qx-util';
import { IPSModelObject } from './ipsmodel-object';
import { IPSModelService } from './ipsmodel-service';
import { ModelServiceStore } from './core';
import { IPSModelServiceProxy } from './ipsmodel-service-proxy';
import { IPSAppDataEntity, IPSAppView, IPSControl, IPSDEServiceAPIRS } from './exports';
import { GlobalHooks } from './core/hooks';

/**
 * 模型服务
 *
 * @export
 * @abstract
 * @class PSModelServiceImplBaseBase
 * @implements {IPSModelService}
 */
export abstract class PSModelServiceImplBaseBase implements IPSModelService {
  /**
   * 数据缓存对象
   *
   * @author chitanda
   * @date 2021-12-26 16:12:44
   * @protected
   * @type {ModelServiceStore}
   */
  protected store: ModelServiceStore = new ModelServiceStore();

  /**
   * Creates an instance of PSModelServiceImplBaseBase.
   *
   * @author chitanda
   * @date 2021-12-26 17:12:03
   * @param {(modelPath: string) => IModel} loadModel 加载模型方法
   */
  constructor(protected loadModel: (modelPath: string) => IModel) {
    this.onInit();
  }

  /**
   * 获取根节点模型对象,如: 系统 or 应用
   *
   * @author chitanda
   * @date 2022-08-09 10:08:00
   * @return {*}  {IPSModelObject}
   */
  getRootModel(): IPSModelObject {
    throw new Error('Method not implemented.');
  }

  /**
   * 初始化
   *
   * @author chitanda
   * @date 2022-03-06 17:03:25
   */
  onInit(): void {}

  /**
   * 模型缓存
   *
   * @author chitanda
   * @date 2022-08-09 10:08:45
   * @return {*}  {ModelServiceStore}
   */
  getStore(): ModelServiceStore {
    return this.store;
  }

  /**
   * 获取模型对象
   *
   * @param {string} cls
   * @param {string} strPath
   * @return {*}  {Promise<IPSModelObject>}
   */
  getPSModel(cls: string, strPath: string, refObj?: IModel, relatedPSModelObject?: IPSModelObject): IPSModelObject {
    // 从缓存获取
    const iPSModelObject = this.store.modelObject.get(strPath);
    if (iPSModelObject != null) {
      return iPSModelObject;
    }
    const model = this.loadModel(strPath);
    if (model) {
      // 创建模型对象
      const psModelObj = this.createPSModelObject(cls, refObj, relatedPSModelObject);
      if (psModelObj != null) {
        const iPSModelService = this.getPSModelService(cls, psModelObj);
        psModelObj.init(iPSModelService, strPath, model, relatedPSModelObject);
        this.store.setModelCache(cls, strPath, psModelObj);
        return psModelObj!;
      }
      throw new Error(`模型对象[${cls}]创建失败!`);
    }
    throw new Error(`模型[${strPath}]加载失败!`);
  }

  /**
   * 根据类型建立模型对象
   *
   * @protected
   * @param {IPSModelObject} parentPSModelObject
   * @param {string} cls
   * @param {IModel} [objNode]
   * @return {*}  {IPSModelObject}
   */
  protected createPSModelObject(cls: string, objNode?: IModel, parentPSModelObject?: IPSModelObject): IPSModelObject {
    return this.onCreatePSModelObject(parentPSModelObject!, cls, objNode)!;
  }

  /**
   * 创建指定对象
   *
   * @protected
   * @param {IPSModelObject} _parentPSModelObject
   * @param {string} _cls
   * @param {IModel} [_objNode]
   * @return {*}  {IPSModelObject}
   */
  protected onCreatePSModelObject(_parentPSModelObject: IPSModelObject, _cls: string, _objNode?: IModel): IPSModelObject | null {
    throw new Error(`模型对象[${_cls}]创建失败!`);
  }

  /**
   * 根据类型，获取模型服务
   *
   * @author chitanda
   * @date 2021-12-26 15:12:25
   * @protected
   * @param {string} cls
   * @param {IPSModelObject} [modelObject]
   * @return {*}  {IPSModelService}
   */
  protected getPSModelService(cls: string, modelObject?: IPSModelObject): IPSModelService {
    if (modelObject) {
      const iPSModelService = this.store.modelService.get(modelObject.cls);
      if (iPSModelService == null) {
        const iPSModelServiceProxy = this.createPSModelServiceProxy(cls, modelObject);
        if (iPSModelServiceProxy != null) {
          iPSModelServiceProxy.init(this, modelObject.cls);
          this.store.modelService.set(modelObject.cls, iPSModelServiceProxy);
          return iPSModelServiceProxy;
        }
        this.store.modelService.set(modelObject.cls, this);
        return this;
      }
      return iPSModelService;
    }
    return this;
  }

  /**
   * 根据类型创建模型代理类
   *
   * @protected
   * @param {string} _cls
   * @param {IPSModelObject} _modelObject
   * @return {*}  {IPSModelServiceProxy}
   */
  protected createPSModelServiceProxy(_cls: string, _modelObject: IPSModelObject): IPSModelServiceProxy | null {
    return null;
  }

  /**
   * 根据路径获取模型
   *
   * @author chitanda
   * @date 2021-12-26 17:12:50
   * @param {IPSModelObject} _relatedPSModelObject
   * @param {string} filePath
   * @return {*}  {IModel}
   */
  getModel(_relatedPSModelObject: IPSModelObject, filePath: string): IModel {
    return this.loadModel(filePath);
  }

  /**
   * 查找指定模型
   *
   * @author chitanda
   * @date 2021-06-08 20:06:44
   * @param {IPSModelObject} relatedPSModelObject 相关模型
   * @param {string} cls 类型标识
   * @param {IModel} obj 模型
   * @param {string} [strTag] 方法标识
   * @return {*}  {IPSModelObject}
   */
  getPSModel4(relatedPSModelObject: IPSModelObject, cls: string, obj: IModel, strTag?: string): IPSModelObject {
    const modelObject = GlobalHooks.getModel4.call(relatedPSModelObject, cls, obj, strTag);
    if (modelObject) {
      return modelObject;
    }
    try {
      const bModelRef = obj.modelref || false;
      if (bModelRef) {
        if (notNilEmpty(obj.path)) {
          const strPath = obj.path;
          return this.getPSModel(cls, strPath, obj, relatedPSModelObject);
        }
        // 相对父模型
        if (relatedPSModelObject != null) {
          return relatedPSModelObject.getChildPSModelObject(cls, obj, strTag);
        }
      } else {
        const path = obj.path || obj.dynaModelFilePath;
        if (notNilEmpty(path)) {
          const strPath = path;
          const iPSModelObject = this.store.modelObject.get(strPath);
          if (iPSModelObject == null) {
            const t = this.createPSModelObject(cls, obj, relatedPSModelObject);
            this.store.setModelCache(cls, strPath, t);
            t.init(this.getPSModelService(cls, t), '', obj, relatedPSModelObject);
            return t;
          }
          return iPSModelObject;
        }
        const t = this.createPSModelObject(cls, obj, relatedPSModelObject);
        t.init(this.getPSModelService(cls, t), '', obj, relatedPSModelObject);
        return t;
      }
    } catch (err: any) {
      throw new Error(`异常: ${err.message}，类型「${cls}」`);
    }
    throw new Error(`类型「${cls}」模型创建异常`);
  }

  /**
   * 从集合里边提取，符合目标key的对象
   *
   * @param {IPSModelObject} _relatedPSModelObject 相关模型
   * @param {string} cls 目标对象
   * @param {IPSModelObject[]} list 目标集合
   * @param {*} data 需要查找的key
   * @return {*}
   */
  getPSModel5(_relatedPSModelObject: IPSModelObject, cls: string, list: IPSModelObject[], data: string | IModel) {
    if (list == null || data == null) {
      if (data != null) {
        const strTag = this.getPSModelTag(cls, data);
        throw new Error(`无法获取指定模型[${cls}]，标识为[${strTag}]`);
      } else {
        throw new Error(`无法获取指定模型[${cls}]，没有指定标识`);
      }
    }
    const strTag: string = this.getPSModelTag(cls, data);
    for (const item of list) {
      if (this.testPSModel(cls, item, strTag)) {
        return item;
      }
    }
    throw new Error(`无法获取指定模型[${cls}]，标识为[${strTag}]`);
  }

  /**
   * 测试复合目标对象
   *
   * @protected
   * @param {string} _cls
   * @param {IPSModelObject} obj
   * @param {string} strTag
   * @return {*}  {boolean}
   */
  protected testPSModel(_cls: string, obj: IPSModelObject, strTag: string): boolean {
    if (notNilEmpty(strTag) && notNilEmpty(obj)) {
      switch (strTag) {
        case obj.id:
          // case obj.modelPath:
          // case obj.codeName:
          // case obj.name:
          return true;
        default:
          return false;
      }
    }
    return false;
  }

  /**
   * 获取模型标识
   *
   * @protected
   * @param {string} cls
   * @param {(string | IModel)} objKey
   * @return {*}  {string}
   */
  protected getPSModelTag(cls: string, objKey: string | IModel): string {
    if (typeof objKey === 'string') {
      return objKey as string;
    }
    if (isObject(objKey)) {
      const obj = objKey as IModel;
      if (obj.modelref === true) {
        let strTag = obj.path;
        if (isNilOrEmpty(strTag)) {
          strTag = obj.id;
          if (isNilOrEmpty(strTag)) {
            throw new Error(`无法计算模型[${cls}]标识`);
          }
        }
        return strTag;
      }
      let strTag = obj.dynaModelFilePath;
      if (notNilEmpty(strTag)) {
        return strTag;
      }
      strTag = obj.codeName;
      if (notNilEmpty(strTag)) {
        return strTag;
      }
      strTag = obj.name;
      if (notNilEmpty(strTag)) {
        return strTag;
      }
    }
    throw new Error(`无法计算模型[${cls}]标识`);
  }

  /**
   *
   *
   * @param {IModel} obj
   * @return {*}  {IModel}
   */
  getRealObjectNode(obj: IModel): IModel {
    return obj;
  }

  /**
   * 查找子模型
   *
   * @author chitanda
   * @date 2022-10-20 11:10:16
   * @param {IPSModelObject} relatedPSModelObject
   * @param {string} cls
   * @param {IModel} obj
   * @param {string} strTag
   * @return {*}  {IPSModelObject}
   */
  getChildPSModelObject(relatedPSModelObject: IPSModelObject, cls: string, obj: IModel, strTag: string): IPSModelObject {
    const modelObject = GlobalHooks.getChildPSModelObject.call(relatedPSModelObject, cls, obj, strTag);
    if (modelObject) {
      return modelObject;
    }
    throw new Error('getChildPSModelObject Method not implemented.');
  }

  /**
   * 获取指定类型父模型，如果本身模型符合接口返回自身
   *
   * @param {IPSModelObject} relatedPSModelObject
   * @param {string} cls
   * @return {*}  {IPSModelObject}
   */
  getParentPSModelObject(relatedPSModelObject: IPSModelObject, cls: string): IPSModelObject;

  getParentPSModelObject(relatedPSModelObject: IPSModelObject, cls: string, tryMode?: boolean): IPSModelObject;

  getParentPSModelObject(relatedPSModelObject: IPSModelObject, cls: string, tryMode = true): IPSModelObject {
    const t = this.getParentPSModelObject2(relatedPSModelObject, cls);
    if (t == null) {
      // 应用
      if (cls === 'app.IPSApplication') {
        if (relatedPSModelObject.instanceof('app.IPSApplication')) {
          return relatedPSModelObject;
        }
      }
      // 实体
      else if (cls === 'dataentity.IPSDataEntity') {
        if (relatedPSModelObject.instanceof('dataentity.IPSDataEntity')) {
          return relatedPSModelObject;
        }
        const ipsAppDataEntity = this.getParentPSModelObject(relatedPSModelObject, 'app.dataentity.IPSAppDataEntity') as IPSAppDataEntity;
        if (ipsAppDataEntity) {
          return ipsAppDataEntity.getPSDataEntityMust();
        }
      }
      // 应用实体
      else if (cls === 'app.dataentity.IPSAppDataEntity') {
        if (relatedPSModelObject.instanceof('control.IPSControl')) {
          return (relatedPSModelObject as IPSControl).getPSAppDataEntityMust();
        }
        if (relatedPSModelObject.instanceof('app.view.IPSAppView')) {
          return (relatedPSModelObject as IPSAppView).getPSAppDataEntityMust();
        }
        const ctrl = this.getParentPSModelObject(relatedPSModelObject, 'control.IPSControl', false) as IPSControl;
        if (ctrl) {
          return ctrl.getPSAppDataEntityMust();
        }
        const view = this.getParentPSModelObject(relatedPSModelObject, 'app.view.IPSAppView', false) as IPSAppView;
        if (view) {
          return view.getPSAppDataEntityMust();
        }
      }
      // 实体服务接口
      else if (cls === 'dataentity.service.IPSDEServiceAPI') {
        const iPSDEServiceAPIRS = this.getParentPSModelObject(relatedPSModelObject, 'dataentity.service.IPSDEServiceAPIRS') as IPSDEServiceAPIRS;
        if (iPSDEServiceAPIRS) {
          return iPSDEServiceAPIRS.getMinorPSDEServiceAPIMust();
        }
        if (relatedPSModelObject.instanceof('dataentity.service.IPSDEServiceAPIMethod')) {
          const p = relatedPSModelObject.getParentPSModelObject();
          if (p && p.instanceof('dataentity.service.IPSDEServiceAPIRS')) {
            return (p as IPSDEServiceAPIRS).getMinorPSDEServiceAPIMust();
          }
        }
      }
      if (tryMode) {
        throw new Error(`无法从模型[${relatedPSModelObject.cls}]获取指定类型[${cls}]父模型`);
      }
    }
    return t as IPSModelObject;
  }

  protected getParentPSModelObject2(relatedPSModelObject: IPSModelObject, cls: string): IPSModelObject | null {
    if (relatedPSModelObject) {
      if (relatedPSModelObject.instanceof(cls)) {
        return relatedPSModelObject;
      }
      const p = relatedPSModelObject.getParentPSModelObject();
      if (p) {
        return this.getParentPSModelObject2(p, cls);
      }
    }
    return null;
  }

  getFirstPSModelFilePath(relatedPSModelObject: IPSModelObject): string | null {
    const strFilePath = relatedPSModelObject.modelFilePath;
    if (notNilEmpty(strFilePath)) {
      return strFilePath!;
    }
    if (relatedPSModelObject.getParentPSModelObject() != null) {
      return this.getFirstPSModelFilePath(relatedPSModelObject.getParentPSModelObject());
    }
    return null;
  }
}
